package model.micro.projectiles
{
	import model.micro.Battle;
	import model.micro.FieldUnit;
	import model.micro.SolidObject;
	import util.Settings;
	
	/**
	 * A projectile launched from a weapon. Path is determined by initial
	 * velocity, gravity, air resistance and wind.
	 */
	public class Projectile
	{
		/** Acceleration due to gravity */
		static public const GRAVITY:Number = -9.8 * Settings.METER;
		/** Value speed is multiplied by for air resistance */
		static public const AIR_RESISTANCE:Number = 0.003;
		// TODO: Get wind values from battle.
		/** Acceleration in X direction due to wind */
		static public const WIND_X:Number = 0.01 * Settings.METER;
		/** Acceleration in Y direction due to wind */
		static public const WIND_Y:Number = 0 * Settings.METER;
		/** Multiplier for damage done based on weight and speed.*/
		static public const DAMAGE_CONSTANT:Number = 0.1;
		
		/** World x coordinate */
		private var x:Number;
		/** World y coordinate */
		private var y:Number;
		/** Speed in the x direction in pixels per frame */
		private var xspeed:Number;
		/** Speed in the x direction in pixels per frame */
		private var yspeed:Number;
		/** Type of projectile */
		private var projectile_type:ProjectileType;
		/** True when this object should be removed. */
		private var ready_to_remove:Boolean;
		
		/**
		 * Create a new projectile.
		 * @param	x					world coordinate
		 * @param	y					world coordinate
		 * @param	xspeed
		 * @param	yspeed
		 * @param	projectile_type		for determining damage and everything
		 */
		public function Projectile(x:Number, y:Number, xspeed:Number, yspeed:Number, projectile_type:ProjectileType)
		{
			this.projectile_type = projectile_type;
			this.x = x;
			this.y = y;
			this.xspeed = xspeed;
			this.yspeed = yspeed;
			
			// offset by up to one frame of movement to avoid arrows clumping by frame.
			this.move(Math.random());
		}
		
		/**
		 * Update by one frame
		 * @param	battle
		 */
		public function update(battle:Battle):void
		{
			// BUG: arrows move through targets with large timestep.
			
			var iterations:int = Math.sqrt(xspeed * xspeed + yspeed * yspeed) / Settings.getTimeStep();
			
			move();
			
			if (battle.ground.getBoundingBox().testPoint(x, y))
			{
				onHit(battle.ground);
			}
			
			if (true)
			{
				for each (var unit:FieldUnit in battle.attack_field_units)
				{
					if (unit.getBoundingBox().testPoint(x, y))
					{
						onHit(unit);
					}
				}
				
				for each (unit in battle.defense_field_units)
				{
					if (unit.getBoundingBox().testPoint(x, y))
					{
						onHit(unit);
					}
				}
				if (battle.fortress.getBoundingBox().testPoint(x, y))
				{
					onHit(battle.fortress);
				}
			}
			if (y < -100)
			{
				ready_to_remove = true;
			}
		}
		
		/**
		 * Move the arrow.
		 * @param	percent	The fraction of the timestep to simulate movement for. Defaults to 1.0.
		 */
		protected function move(percent:Number = 1.0):void
		{
			var timestep:Number = Settings.getTimeStep() * percent;
			x += xspeed * timestep;
			y += yspeed * timestep;
			yspeed += (GRAVITY * timestep);
			xspeed += (WIND_X * timestep);
			yspeed += (WIND_Y * timestep);
			xspeed *= 1 - (AIR_RESISTANCE * timestep);
			yspeed *= 1 - (AIR_RESISTANCE * timestep);
		}
		
		/**
		 * Called when the arrow hits a solid object.
		 * @param	hit
		 */
		public function onHit(hit:SolidObject):void
		{
			hit.onHit(this);
			ready_to_remove = true;
		}
		
		/**
		 * @return	true if the projectile should be deleted.
		 */
		public function readyToRemove():Boolean
		{
			return ready_to_remove;
		}
		
		/**
		 * @return	X world coordinate
		 */
		public function getX():Number
		{
			return x;
		}
		
		/**
		 * @return	Y world coordinate
		 */
		public function getY():Number
		{
			return y;
		}
		
		/**
		 * @return	X speed
		 */
		public function getXSpeed():Number
		{
			return xspeed;
		}
		
		/**
		 * @return	Y speed
		 */
		public function getYSpeed():Number
		{
			return yspeed;
		}
		
		/**
		 * @return	The projectile type of this projectile
		 */
		public function getType():ProjectileType
		{
			return projectile_type;
		}
		
		/**
		 * Returns the amount of damage done at the current speed.
		 * @return
		 */
		public function getDamage():Number
		{
			return Math.sqrt(xspeed * xspeed + yspeed * yspeed) * projectile_type.weight * DAMAGE_CONSTANT;
		}
	}
}